 
/*      ZAPAL.C - Byte Magazine ZAP-A-PAL Programmer for IBM-PC		*/

/*	Version 2.0 - by Robert A. Freedman - 24 Dec 1986 - 3:00 AM	*/

/* COPYRIGHT Notice: (C) 1986 by Robert A. Freedman. All RIGHTS Reserved,
A limited license is granted to each reader of Byte Magazine to copy this
program for private use in conjunction with the construction of a single unit
of the ZAP-A-PAL programmer board described in the January 1987 issue of Byte
Magazine, provided that this notice is included in any copy made.
Commercial resale or distribution of this program is expressely prohibited
without written permission of the author.

	Note:	This is NOT Public Domain Software.			*/

/* This version of ZAPAL is set up to take JEDEC files for PALs which use the
whole fuse array, like 16L8 etc. Sparse array PALs like the 10L8, 14H4, etc. can
be programmed, but you have to edit the JEDEC fuse file to put in the phantom
fuses that most logic compilers leave out in a misguided attempt at efficiency.
To see what the JEDEC file should look like, put a blank sparse PAL into the
socket and (R)ead it into the buffer. Then look at the fuse array with (Y).
The phantom fuses appear as blown. Those columns should be edited into the
JEDEC file produced by the Logic compiler before being loaded into ZAPAL.
You then have to correct the fuze numbers in the JEDEC file to conform to the
full fuse array after editing. Phantom rows are normally supplied by ZAPAL.
The diagrams for sparse array PALs in the PAL Handbook show this clearly.
Don't worry that editing the JEDEC file will blow the checksum. ZAPAL doesn't
bother to verify the checksum in the JEDEC file anyway. Later versions of ZAPAL
will tweek the fusemap for sparse PALs for you, but hey, I had to leave
something to challenge the hobbiest. It'd be no fun if I did everything for you.
Besides, if you stick with 16L8, 16R4-6-8, 20L8, etc. like I do, ZAPAL works OK.
Another thing to watch is that your logic compiler produces the proper polarity
for true output PALs like 14H4 or 10H8. You may have to invert the sense of the
fuse in this program if it is incorrect. If there is enough interest, I will
make updated versions of ZAPAL available for Byte to distribute as the program
develops.

	Any questions or comments?

Bob Freedman, PO Box 1348, Lawrence, Mass. 01842. (617) 683-4659
*/
 
#include <stdio.h>

#define uchar unsigned char
#define ERROR -1
#define base  0x100	/* Zap-A-PAL Address Space is from 100 -> 10F hex */
#define DAC_A base+0
#define DAC_B base+1
#define SCLK  base+2
 
#define STROBE base+0x8
#define ENAB   base+0x9		/* Enable BiMOS drivers	*/
#define ENCH   base+0xA
#define ENCL   base+0xB
#define VLH    base+0xD
#define VINHIB base+0xE
#define TRIG   base+0xF
 
#define BUSY		(~inportb(base+0xC) & 1)
		/*	 (inportb(base+0xC) ^ 1) & 1	*/
#define CAL_REF_10V       inportb(base+0xA) & 1
#define	CAL_REF_2P5       inportb(base+0xB) & 1
#define P23 base+0x8
#define P22 base+0x0
#define P21 base+0x1
#define P20 base+0x2
#define P19 base+0x3
#define P18 base+0x4
#define P17 base+0x5
#define P16 base+0x6
#define P15 base+0x7
#define P14 base+0x9
 
int c,e;
static float slope_a,slope_b,vcc_want,ver_want,vihh_want;

static int vcclo,vcchi,vihlo,vihhi;	/* Auto-Cal Points	*/
       int dac,dac_vcc,dac_vihh;
       int vcc,ver,verlo,verhi,vihh;	/* DAC settings		*/	

static float  actual_10V = 10.110 ;	/* Measured values of	*/
static float  actual_2P5 =  2.463 ;	/* Voltage References	*/

static float  vcc_offset = 0.000+0.7 ;	/* Compensate for diode drops	*/
static float vihh_offset = 1.130+0.7 ;	/* in Drivers & at GND Pin	*/

static int verpin,vad,fuse;	/* Pin # to verify, I/O adr, State	*/
static int veradr[10] = {9,7,6,5,4,3,2,1,0,8};	/* Mux adr for Pins 14 - 23 */
 
uchar pins[32];
/*		Outputs		 Control		Inputs
	 2 2 2 1 1 1 1 1 2 1    1                              1 1
         2 1 0 9 8 7 6 5 3 4  1 3              2 3 4 5 6 7 8 9 0 1	*/
static uchar clear[28] =
	{0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0}; /* Clear */
static uchar odlo[28] =
	{0,0,0,0,0,0,0,0,0,0, 2,1,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0}; /* OD lo */
static uchar odhi[28] =
	{0,0,0,0,0,0,0,0,0,0, 1,2,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0}; /* OD hi */

static int pind[24] =	/* Maps Pin numbers to Shift Register Position	*/
	{10,18,19,20,21,22,23,24,25,26,27,28,11,9,7,6,5,4,3,2,1,0,8};
 
static s;		/* s is Style of fuze-map print-out	*/

int cksum;	/* Check-Sum for JEDEC file	*/
int devno;	/* 'D'nnnn read from Basic JEDEC file	*/

struct	mfg_attr {
char	*mfg_nam;
float	verlo,verhi,vihh; } m[] = {

{ {" MMI"},		5.0, 5.5, 11.75 },
{ {"-B MMI"},		6.0, 6.0, 10.00 },
{ {" TI "},		5.0, 5.0, 10.50 },
{ {" NSC"},		5.0, 5.5, 11.75 }
};

int ci,chip_index;
int mi,mfg_index;

struct	chip_type {
char	*chip_nam;
int	npins,fuzes; } t[] = {

{ {"16L8"},		20, 2048 },
{ {"16R8"},		20, 2048 },
{ {"16R6"},		20, 2048 },
{ {"16R4"},		20, 2048 },
{ {"10H8"},		20, 2048 },
{ {"10L8"},		20, 2048 },
{ {"12H6"},		20, 2048 },
{ {"12L6"},		20, 2048 },
{ {"14H4"},		20, 2048 },
{ {"14L4"},		20, 2048 },
{ {"16H2"},		20, 2048 },
{ {"16L2"},		20, 2048 },
{ {"16C1"},		20, 2048 },
{ {"16X4"},		20, 2048 },
{ {"16A4"},		20, 2048 },
{ {"12L10"},		24, 3200 },
{ {"14L8"},		24, 3200 },
{ {"16L6"},		24, 3200 },
{ {"18L4"},		24, 3200 },
{ {"20L2"},		24, 3200 },
{ {"20C1"},		24, 3200 },
{ {"20L10"},		24, 3200 },
{ {"20X10"},		24, 3200 },
{ {"20X8"},		24, 3200 },
{ {"20X4"},		24, 3200 },
{ {"20L8"},		24, 3200 },
{ {"20R8"},		24, 3200 },
{ {"20R6"},		24, 3200 },
{ {"20R4"},		24, 3200 }
};

unsigned seg,offset,off,word;
struct regval { int ax,bx,cx,dx,si,di,ds,es; };
struct regval *sreg,*rreg;

int     n,n_wr,i,j,sum,lr,ix,ino,af,T20,T24,fuzno,max;
char    *fn,filename[64],*p,fin[32],fon[32];

extern FILE *fopen();
FILE    *fo,*fp;

static int fuzmap[4096];

int test_fuz;	/* for TEST program	*/

main(argc,argv) int argc; char **argv;
{
	strcpy(fin,"file.jed");	/* Init Dummy file.names	*/
	strcpy(fon,"fmap.jed");

	T20 = 1;	/* Do 20-pin PALs	*/
	ci = mi = 0;	test_fuz = 0;	s = 5;

/*	dac_vcc  = 131;	*/	/* DAC-A 131 = 5.02 Volts	*/
/*	dac_vihh = 154;	*/	/* DAC-B 154 = 11.75 Volts: 13.00 @ VIHH */

	autocal(0);	shutdn();

	menu();		shutdn();	exit(0); 
 }
menu()		/* Display List of ZAPAL Commands	*/
{
int szct,szmt;	/* number of entries in structures.	*/

uchar *b; b="                                        ";
	wipe();	/* Clear Screen	*/
		szct = sizeof(t) / sizeof(struct chip_type);
		szmt = sizeof(m) / sizeof(struct  mfg_attr);

	while ( 1 ) {

 	crt_srcp(0,0,0);	printf(
"\tZAP-A-PAL Programmer - (C) 1986 by R. A. Freedman S# 00001        ");
	T20 = ( t[ci].npins==20 ? 1 : 0 );
	printf("\n\n\t\t%PAL-%s %s has %2d pins and %4d fuzes        \n",
		t[ci].chip_nam,m[mi].mfg_nam,t[ci].npins,t[ci].fuzes );

	printf("\n\tT - Select Device Type:");
	printf("\n\tU - Select Chip Manufacturer:");
	printf("\n\tL - Load PAL chip into RAM");
	printf("\n\tR - Show fuze map from PAL Chip");
	printf("\n\tY - Show fuze map from RAM");
	printf("\n\tC - Compare PAL chip with RAM");
	printf("\n\tZ - Burn  a PAL from RAM");
	printf("\n\tV - Verify  PAL against RAM");
	printf("\n\tI - Set to READ  file %s        ",fin);
	printf("\n\tO - Set to WRITE file %s        ",fon);
	printf("\n\tJ - Load  JEDEC file into RAM *");
	printf("\n\tW - Write JEDEC file from RAM");
	printf("\n\tS - Style of Fuze MAP is %s ",(s&1?"JEDEC":"PALASM"));
	printf("\n\tF - FUZEs are shown as %s",(s&2?"'1' or '0'":"'X' or '-'"));
	printf("\n\tG - %s GAP every 4th fuse",(s&4?"Insert":"Don't "));
/*	printf("\n\tM - Calibrate manually");	*/
	printf("\n\tA - Auto-Calibrate");
	printf("\n\tQ - Quit to DOS \t\t");
	printf("\n%s%s%s%s%s%s",b,b,b,b,b,b); 	crt_srcp(21,0,0);


		c = tolower(bdos(1) & 0xFF);	/* Read a Key	*/
		switch(c) {

		case 'm': calib();	break;
		case 'a': autocal(1);	break;
		case 'q': return(0);	break;
		case 'r': shopal(1,s);	break;	/* Show Fuz-Map from chip */
		case 'y': shopal(0,s);	break;	/* show Fuz-Map from RAM  */
		case 'e': test();	break;
		case 'z': zapal();	break;
		case 'v': mount(); veripal();	break;
		case 'l': loadpal();	break;
		case 'j': if(loadjedec()==ERROR){
				printf("ERROR"); getchar(); }; break;
		case 'w': write_jedec();	break;
		case 'p': paltype();	break;
		case 'c': shopal(2,s);	break;	/* Compare PAL with RAM	*/
		case 's': s = s ^ 1;	break;
		case 'f': s = s ^ 2;	break;
		case 'g': s = s ^ 4;	break;
		case 't': ci= (ci+1) % szct;	break;
		case 'u': mi= (mi+1) % szmt;	break;
		case 'i': getfn(fin);	break;
		case 'o': getfn(fon);	break;
		case 's': s = s ^ 1;	break;

		default :		break;
		}; shutdn();

	};
}
getfn(fn) char *fn;	/* Get a file name	*/
{char *p; int l;
		printf(" Enter File.nam ");
		fgets(fn,30,stdin); l=strlen(fn);
		p = fn + l - 1;  *p = 0; /* zot \n	*/
}
wipe()
{
	printf("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n");
}
paltype(){}
loadjedec()	/* Load a JEDEC file into RAM	*/
{
	fp = fopen(fin,"r");
		if (!fp) { erra(fp,"\nCan't OPEN file ",fin); return(0); };

	for (fuzno=0; fuzno < (T20?2048:3200); fuzno++) {
		fuzmap[fuzno] = 2;	/* Mark as Empty	*/
	};

	if (star() == ERROR) return(ERROR);	/* scan for '*'	*/

	while (1) {
	
		if (white() == ERROR) return(ERROR);

		/* c must have command letter, Q G F L C #	*/

		if (c == 'Q') { c = fgetc(fp);
			if (c == 'P')	/* max # of pins on chip   */
					{				};
			if (c == 'F')	/* max # of fuses in array */
					{				};
			if (star() == ERROR) return(ERROR); /* scan for '*' */
			continue;
		};

		if (c == 'G')	/* State of Security Fuze	*/
				{ fgetc(fp); };

		if (c == 'F')	/* Default for undefined fuses	*/
				{ fgetc(fp); };

		if (c == 'C')   /*   CheckSum	*/
				{ fclose(fp); return(0); };

		if (c == 03)   /*   other kind of CheckSum	*/
				{ fclose(fp); return(0); };

		if (c == 'D') {
			devno = 0; for (i=0;i<4;i++) { c = fgetc(fp);
				devno = devno * 10 + ani(c);	}; };

		if (c == 'L') {
			fuzno = 0; for (i=0;i<4;i++) { c = fgetc(fp);
				fuzno = fuzno * 10 + ani(c);	};

			while ( (c=white()) == '1' || c == '0')
				{ fuzmap[fuzno] = c & 1; fuzno++; };
		};
	};
}
white()	/* eat chars till c is no longer whitespace	*/
{		do {	if ((c=fgetc(fp))==ERROR) { fclose(fp);
				erra(c,"while eating white-space","");
				return(ERROR); };
		} while ( isspace(c) ); return(c);
}
star()	/* Scan til you find an '*' */
{	while ( (c=fgetc(fp)) != '*') {
			if (c==ERROR) { fclose(fp);
			erra(c,"looking for '*'",""); return(ERROR); };
	}; return(c);
}
write_jedec()	/* Write Fuze-Map to disk in JEDEC Format	*/
{
	int f;
	fp = fopen(fon,"w");
		if (!fp) { erra(fp,"\nCan't OPEN file ",fon); return(0); };

	for (j=0;j<(T20?2048:3200);j++) {
		f = fuzmap[j];

		if ( j % (T20?32:40) == 0 ) {
			 if (f == 2) { j = j + (T20?32:40) - 1; continue; }; 
			 fprintf (fp,"\n*L%04d ",j);
		};

		fprintf(fp,"%c",( (f & 1) ? '1':'0') );
	};	fprintf(fp,"\n*C%04d\n",cksum);
		fclose (fp);
}
char bcda(p) char *p; {return(ani(*p++)*16+ani(*p++)); }
int ani(c) char c; { return( c >= 'A' ? (c-'7') : (c-'0') ); }

loadpal()	/* Load from PAL chip into RAM	*/
{
	mount();  max = (T20?2048:3200);
	for (fuzno=0; fuzno < max; fuzno++) {
		if(fuzno%256 == 0) printf("\rLoading %3d %%",percent(fuzno,max) );
		fuzmap[fuzno] = (~readfuz( fuzno )) & 1;
	}; shutdn();
}
mount()
{	printf("\nPlease mount PAL in Zif Socket, Watch Pin 1");
		getchar();
		revup();	/* Turn ON the Power	*/
}
shopal(f,s) int f,s;
/*	  f = source:	0 = from RAM
			1 = from PAL chip
			2 = show differences

	  s =	Style:	0
			1 = JEDEC vs PALASM numbering
	  bits		2 = "1" & '0'vs 'X' & `-`
			4 = Space every 4 bits
			8 = Gap every 8 lines		*/
			
{	int fooz,fuz,ill,j,m;

	if(f != 0) mount(); s = s & 7;

	fuzno=0;  while ( fuzno < (T20?2048:3200) ) {

		ill = (T20?32:40);
		if (bdos(11) & 0xFF) break;	/* Escape from long listing */
		if( (fuzno/ill)%8 && s&4) printf("\n");

		printf ( (s&1?"\n*L%04d ":"\n%4d "),
					(s&1?fuzno:fuzno/(T20?32:40) ) );
		for (j=0;j<(T20?32:40);j++) {
		f = f & 3; m = (~fuzmap[fuzno]) & 1;
		if(f == 0) { fooz = m; };
		if(f == 1) { fooz = readfuz(fuzno); };
		if(f == 2) { fooz = m ^ readfuz(fuzno); };

	printf("%c",(f==2?
			(!fooz ? '.' : (m ? 'O':'U') ):
			(s&2?(fooz?'1':'0'):(fooz?'X':'-') )
		    )
		);
			fuzno++;
			if ( !(fuzno % 4) ) printf((s&4?" ":""));
		};
	}; shutdn();	printf("\tHit any key"); key(); wipe();

}
autocal(ar) int ar;	/* Automatic Calibration of Voltage Generators	*/
{	int d,test;		/* ar =  0: autotmatic	*/
	float a;		/* ar = !0: optional	*/

	outportb(DAC_A,0);	/* Load DAC-A = 0 V	*/
	outportb(DAC_B,0);	/* Load DAC-B = 0 V	*/

	printf("\nPlease Remove PAL from socket during Calibration");
		getchar();
				/* Turn ON the Power	*/
	outportb(ENAB,0);	/* Disable BiMOS drivers 	*/
	outportb(ENCL,1);	/* Disable BiMOS drivers CLOCK	*/
	outportb(ENCH,1);	/* Disable BiMOS drivers OD	*/

        outportb(VINHIB,1);     /* Turn ON 497 Booster	*/
	outportb(VLH,0);	/* Set Booster to Low (15V)	*/
	outportb(TRIG,0);	/* Make sure TRIG is low	*/
 
	test = 0; d = 0; dac = 0;

		vcclo = slew(0,0); if (vcclo == ERROR) return(ERROR);

		vcchi = slew(0,1); if (vcchi == ERROR) return(ERROR);

		dac--;	slew(0,1);

	outportb(DAC_A,0);	/* Load DAC-A = 0 V	*/
	outportb(DAC_B,0);	/* Load DAC-B = 0 V	*/

	dac = 0;printf("\n");

		vihlo = slew(1,0); if (vihlo == ERROR) return(ERROR);

		vihhi = slew(1,1); if (vihhi == ERROR) return(ERROR);

		dac--;slew(1,1);

	slope_a = (actual_10V - actual_2P5) / (vcchi - vcclo);
	slope_b = (actual_10V - actual_2P5) / (vihhi - vihlo);

	if (!ar) {printf("\nHit ANY key "); key(); return(0); };
	printf("\nSlope_A = %8.4f Slope_B = %8.4f ",slope_a, slope_b);

	for ( a=1.0; a <= 16.0; a+=0.5) { vcc_want = a; vihh_want = a;

	vcc  = vcclo + ( vcc_want - actual_2P5) / slope_a;
	vihh = vihlo + (vihh_want - actual_2P5) / slope_b;

		printf ("\n %4.2f Volts =>  vcc = %4d" ,vcc_want ,vcc);

		printf ("   %4.2f Volts => vihh = %4d",vihh_want,vihh);
	}; key(); wipe();
	
}
int slew(d,test) int d,test;	/*	Scan for Thresholds	*/
{
	do { if (dac > 255) {
		printf("\nCalibration Failure: DAC-%C off Scale",(d?'B':'A') );
		printf("\nProbable Hardware Failure, or Wrong Port Address.");
		return(ERROR); };

		outportb((d?DAC_B:DAC_A),dac);    /* Load DAC-(A or B) */

     dac++;} while ( !(test? CAL_REF_10V : CAL_REF_2P5) );

		printf("\r%s = %3d @ 2.5Volt Point and %3d @ 10.0V Point %3d",
		(d?"vihh":" vcc"),(d?vihlo:vcclo),(d?vihhi:vcchi),dac ); 
		
	return(--dac);		
}

calib()         /* Calibrate Programmable Voltage Sources Manually      */
		/* This is useful for Debugging during construction	*/

{	int d,dac[2]; d = 0; dac[0] = 0; dac[1] = 0;

	printf("\nType 'C' to Calibrate, anything else to use defaults-");
	c = tolower( getchar() ); if (c != 'c' ) return(0);

				/* Turn ON the Power	*/
	outportb(ENAB,0);	/* Disable BiMOS drivers 	*/
	outportb(ENCL,1);	/* Disable BiMOS drivers CLOCK	*/
	outportb(ENCH,1);	/* Disable BiMOS drivers OD	*/

        outportb(VINHIB,1);     /* Turn ON 497 Booster	*/
	outportb(VLH,0);	/* Set Booster to Low (15V)	*/
	outportb(TRIG,0);	/* Make sure TRIG is low	*/
 
	outportb(DAC_A,0);	/* Load DAC-A = 0 V	*/
	outportb(DAC_B,0);	/* Load DAC-B = 0 V	*/

	while (1) {
 
		outportb((d?DAC_B:DAC_A),dac[d]);    /* Load DAC-(A or B) */

	        printf ( "\rvcc=%4d, vihh=%4d %s  %s  ",dac[0],dac[1],
					(CAL_REF_2P5 ? "High":" Low"),
					(CAL_REF_10V ? "High":" Low") );
 
		c = tolower(bdos(1) & 0xFF);	/* Press Key for each step */

		if (c=='q' ) {dac_vcc = dac[0]; dac_vihh = dac[1]; break; };
		if (c=='v') { outportb((d?DAC_B:DAC_A),0);
				 d = ++d & 1;	/* Switch to Other DAC	*/
				}; 
		if (c=='u') dac[d] = ++dac[d] & 0xFF;	/* Up	*/
 		if (c=='d') dac[d] = --dac[d] & 0xFF;	/* Down	*/
 
	};
}
revup()		/* Set up DACs and Turn On Power	*/
{
	outportb(ENAB,0);	/* Disable BiMOS drivers 	*/
	outportb(ENCL,1);	/* Disable BiMOS drivers CLOCK	*/
	outportb(ENCH,1);	/* Disable BiMOS drivers OD	*/

        outportb(VINHIB,1);     /* Turn ON 497 Booster	*/
	outportb(VLH,0);	/* Set Booster to Low (15V)	*/
	outportb(TRIG,0);	/* Make sure TRIG is low	*/
 
	 vcc_want = m[mi].verlo +  vcc_offset;
	 ver_want = m[mi].verhi +  vcc_offset;	
	vihh_want = m[mi].vihh  + vihh_offset;

	verlo = vcc  = vcclo + ( vcc_want - actual_2P5) / slope_a;
	verhi = ver  = vcclo + ( ver_want - actual_2P5) / slope_a;
	        vihh = vihlo + (vihh_want - actual_2P5) / slope_b;

        outportb(DAC_A,vcc);	/* Load DAC-A = vcc Volts	*/

        outportb(DAC_B,vihh);	/* Load DAC-B = VIHH Volts	*/

/*	printf ("\nvcc = %3d - vihh = %3d",vcc,vihh);	*/
}
shutdn()	/* Turn it all off	*/
{
	outportb(ENAB,0);	/* Disable BiMOS drivers 	*/
	outportb(ENCL,1);	/* Disable BiMOS drivers CLOCK	*/
	outportb(ENCH,1);	/* Disable BiMOS drivers OD	*/

        outportb(VINHIB,0);     /* Turn OFF 497 Booster	*/
 
        outportb(DAC_A,0);	/* Load DAC-A  =  0 V	*/

        outportb(DAC_B,0);	/* Load DAC-B  =  0 V	*/
	
}
zapal() 	/* Burn a PAL from RAM image	*/
{
	mount();
        outportb(DAC_A,verlo);		/* Load DAC-A = vcc Volts	*/
	pass(3);	/* Programm Lo	*/
        outportb(DAC_A,verhi);		/* Load DAC-A = vcc Volts	*/
	pass(2);	/* Programm Hi	*/
        outportb(DAC_A,verlo);		/* Load DAC-A = vcc Volts	*/
	pass(1);	/* Programm Lo	*/
	veripal();	/* Verify	*/
	shutdn();
}
pass(n) int n;
{	int m,x;
	printf("\rBurning-%1d  ",n);	
	for (fuzno=0; fuzno <(T20?2048:3200); fuzno++) {
	/*	if (fuzno % (T20?32:40) == 0) printf(".");	*/
		m = fuzmap[fuzno] & 3;
		if ( m >= 2) continue;	/* Skip Phantom Fuze	*/
		if ( m == 0) continue;	/* Skip Wanted  Fuze	*/
		if ( m == 1) {	/* for 16L8 fuze is low if blown	*/
			/* Set-up and see if fuze is already blown	*/
			x = readfuz(fuzno);	/* Read Fuze State	*/
			if (!x) continue;	/* Skip if blown */
		/* All Reprieves Exhausted, Blast Fuze to Oblivion */
			zot();
		}; 
	};	return(0);
}

veripal()	/* Verify PAL chip versus RAM	*/
{
	for (fuzno=0; fuzno < (T20?2048:3200); fuzno++) {
		if (fuzno%64 == 0) printf("\rVerifying-%4d ",fuzno);	

		if( (fuzmap[fuzno] & 1) ^ ((~readfuz(fuzno) & 1)) )
			{ shutdn(); erra(fuzno,"Verify FAILed","");
				return(ERROR); };
	}; shutdn(); printf("\nVerify PASSed");
}
test()	/* Be Caraful with this one */
{	revup();
	while (readfuz(test_fuz) ) { zot(); };
	test_fuz = ++test_fuz & 2048-1;	shutdn();
}
int readfuz(fuzno) int fuzno;	/* Set up to read or write a fuze */
{	int half,pln;		/* Return state of selected fuse  */
	pln = ( fuzno / ( T20 ? 32 : 40 ) );
	if (pln > (T20?63:79) ) return(ERROR);

	outportb(ENAB,0);	/* Disable BiMOS drivers 	*/
	outportb(ENCL,1);	/* Disable BiMOS drivers CLOCK	*/
	outportb(ENCH,1);	/* Disable BiMOS drivers OD	*/

	half = ( T20 ? 32 : 40 );	/* Set OD and CLOCK pins	*/
	pin( 1, (pln >= half?1:2) );	pin(13, (pln >= half?2:1) );

	(pln >= half ? ldsr(odhi) : ldsr(odlo) );	/* Shift OD & Clock */

	outportb(ENCL,0);	/* Enable BiMOS drivers	CLOCK	*/
	outportb(ENCH,0);	/* Enable BiMOS drivers	OD	*/

	selfuz(fuzno);		ldsr(pins);

	outportb(ENAB,1);	/* Enable BiMOS drivers		*/

 	return( verifuz() );	/* Return state of addressed fuze	*/
}
zot()	/* TRIGger the timing PAL to zap the fuze	*/
{		while (  BUSY ) { outportb(TRIG,0); };
		while ( !BUSY ) { outportb(TRIG,1); };
		while (  BUSY ) { outportb(TRIG,0); };
		return(0);
}
int verifuz()	/*	Return state of fuze	*/
{
	/* Assume the shift-registers are all set up by selfuz(fuzno)	*/
	outportb(ENCL,1);	/* Pulse CLOCK pin by floating	*/
	outportb(ENCL,0);	/* CLOCK to Z momentarily	*/

	vad = veradr[verpin-14] + base;	/* Compute Mux adr of Pin	*/
	fuse = inportb(vad) & 1;	/* Read the state of the fuse	*/
	return(fuse);			/* 0 = Blown, 1 = Intact fuse	*/
}
selfuz(fuzno) int fuzno; /* analyzes fuze-number, and sets up all pins */
{       int an,half,of,ox,lino,pl,pln,i;

	half = ( T20 ? 32 : 40 );

	ldsr(clear);	/* Clear out old fuze info	*/

	/*	Compute and place input pins	*/

	lino =( fuzno % half );

	pln = ( fuzno / half );
	if (pln > (T20?63:79) ) return(ERROR);

	lr = 0; if   (lino & 2)  lr = 2;        /* Find which half       */
 
        ix = 0; if (!(lino & 1)) ix = 1;        /* Find the state of Pin x */
 
        /* Now find where to put the selected pin, ie [I0..I9]           */
 
        for (i=0;i<10;i++) pin(2+i,2); /* Pull all input pins to VIHH   */
 
	ino = lino / 4; pin(2+ino,ix);	/* Then set Selected pin to TTL  */
 
	/*	Compute and Place Output Pins	*/
 
	/*	Set OD and CLOCK pins		*/
	pin( 1, (pln >= half?1:2) );	pin(13, (pln >= half?2:1) );

        pl = pln; if(pln >= half) pl = pln-half;
        an =  pl % 8;				/* A0..An = pl mod 8	*/
        ox = (pl / 8) & (T20?0xF:0x1F);		/* Select Outp Pin to pulse */
        for (i=14;i<=23;i++) { pin(i,0); };     /* Clear all Outputs	*/
               
			     af = (T20?16:15 ); of = (T20?22:23 ); 
	if ( pln >= half ) { af = (T20?19:19 ); of = (T20?18:18 ); };
	if ((pln <  half) && !T20 ) an = bitinv(an,4);	
	an = an & (T20? 7 : 0xF );

	for ( i = (T20?2:3); i >= 0; i--) {  /* Set Address bits  */
		pin(af+i,( an % 2 ? 2 : 0 ) ); an = an / 2;
	};
	pin(of-ox,4);   /* Set Output Pin to Pulse      */
	verpin = of-ox;	/* Save pin to verify fuse state	*/
	pin( (pln < half ? (T20?15:14) : (T20?22:23) ),lr); /* Set L/R	*/
	/* Now all the pins are set up for programming or verification	*/
}
int pin(n,val) int n,val;       /* Read or Store value of a pin */
{int v; uchar *p;       if (n == 0 || n > 24 ) val = 0xE;
			p = pins + *(pind + n - 1 );
			v = *p; *p = val; return(v);
}
shopin()
{	printf("\n");
	for (i=0;i<28;i++) {	if( i == 10 || i == 18 ) printf(" -");
		     if( i ==  4 || i ==  8 || i == 23 ) printf(" " );
		printf (" %1x",pins[i] & 0xF ); };
}
copin(p) uchar *p;
{	int i; for (i=0;i<28;i++) { pins[i] = p[i]; };  }

ldsr(p) char *p;        /* Load pins into Hardware Shift Register	*/
{	int i; i=27; while ( i >= 0 ) { outportb(SCLK,p[i--] ); };
        outportb(STROBE,1) ;	/* Strobe all bits into BiMOS latches	*/
        outportb(STROBE,0) ;
}
int bitinv(val,bits) int val,bits;      /* Invert n bits of val */
{       int res; res = 0;
	while (bits) { res = res + res + (val % 2); val = val / 2; bits--;
	};  return(res);
}
int percent(n,max) int n,max;
{	float nn,maxx; nn=n; maxx=max;	return(nn*100.0/maxx);	}
key() /* Hit ANY key	*/
{	uchar c; c = tolower(bdos(1) & 0xFF); return(c);
}
erra(e,p,q) int e; char *p,*q;
{	printf("\rERROR: %d %s%s, Hit CR ",e,p,q); while ( getchar() != '\n') {};
}
/*			That's All Folks    -    R.A.F.			*/

